Shahzroux/audio-mastering-engine
0
1"""2Main Mastering Engine Application3Command-line interface for AI-powered mastering4"""5 6import sys7import os8from pathlib import Path9from typing import Optional10 11# Add src to path12sys.path.insert(0, str(Path(__file__).parent.parent))13 14from analysis.audio_analyzer import AudioAnalyzer15from ai.genre_classifier import SimpleGenreDetector16from ai.preset_system import PresetLibrary17from dsp.processor import MasteringProcessor18 19 20class MasteringEngine:21 """22 Main mastering engine class that coordinates all components23 """24 25 def __init__(self):26 print("๐ต Initializing Mastering Engine...")27 28 self.analyzer = AudioAnalyzer()29 self.genre_detector = SimpleGenreDetector()30 self.preset_library = PresetLibrary()31 self.processor = MasteringProcessor()32 33 print("โ
Engine ready!\n")34 35 def analyze_audio(self, audio_path: str):36 """Analyze audio file"""37 features = self.analyzer.analyze(audio_path)38 self.analyzer.print_analysis(features)39 return features40 41 def suggest_presets(self, audio_path: str, num_suggestions: int = 3):42 """43 Analyze audio and suggest best presets44 """45 print("๐ Analyzing audio and suggesting presets...\n")46 47 # Analyze48 features = self.analyzer.analyze(audio_path)49 50 # Detect genre51 genre_probs = self.genre_detector.detect_genre(features)52 top_genre = max(genre_probs, key=genre_probs.get)53 54 print(f"๐ธ Detected Genre: {top_genre.upper()}")55 print(f" Confidence: {genre_probs[top_genre]*100:.1f}%\n")56 57 # Get presets for this genre58 genre_presets = self.preset_library.get_presets_by_category(top_genre)59 60 if not genre_presets:61 print(f"โ ๏ธ No presets found for genre '{top_genre}'")62 print(" Using general presets instead.\n")63 genre_presets = list(self.preset_library.presets.values())[:3]64 65 # Limit to num_suggestions66 suggested = genre_presets[:num_suggestions]67 68 print(f"๐ก Suggested Presets ({len(suggested)}):")69 for i, preset in enumerate(suggested, 1):70 print(f"\n {i}. {preset.name}")71 print(f" {preset.description}")72 print(f" Target: {preset.target_lufs} LUFS")73 74 print()75 return suggested76 77 def master_audio(self, 78 input_path: str,79 output_path: Optional[str] = None,80 preset_name: Optional[str] = None,81 auto_preset: bool = False):82 """83 Master audio file84 85 Args:86 input_path: Input audio file87 output_path: Output file path (auto-generated if None)88 preset_name: Name of preset to use89 auto_preset: Automatically select best preset90 """91 # Validate input92 if not os.path.exists(input_path):93 print(f"โ Error: File not found: {input_path}")94 return95 96 # Generate output path if not provided97 if output_path is None:98 input_file = Path(input_path)99 output_path = str(input_file.parent / f"{input_file.stem}_mastered{input_file.suffix}")100 101 # Get preset102 if auto_preset:103 print("๐ค Auto-selecting best preset...")104 suggested = self.suggest_presets(input_path, num_suggestions=1)105 preset = suggested[0]106 elif preset_name:107 preset = self.preset_library.get_preset(preset_name)108 if not preset:109 print(f"โ Error: Preset '{preset_name}' not found")110 print(f"\nAvailable presets:")111 for name in self.preset_library.list_all_presets():112 print(f" โข {name}")113 return114 else:115 print("โ Error: Please specify preset_name or use auto_preset=True")116 return117 118 # Process119 print(f"\n๐๏ธ Mastering with: {preset.name}")120 results = self.processor.process_file(input_path, output_path, preset)121 122 # Print summary123 print("\n" + "="*60)124 print("โ
MASTERING COMPLETE!")125 print("="*60)126 print(f"๐ฅ Input: {input_path}")127 print(f"๐ค Output: {output_path}")128 print(f"\n๐ Results:")129 print(f" Input: {results['input_lufs']:.1f} LUFS, Peak: {results['input_peak_db']:.1f} dBFS")130 print(f" Output: {results['output_lufs']:.1f} LUFS, Peak: {results['output_peak_db']:.1f} dBFS")131 print(f" Change: {results['lufs_change']:+.1f} LU")132 print("="*60 + "\n")133 134 return results135 136 def batch_master(self, 137 input_folder: str,138 output_folder: str,139 preset_name: str = None,140 auto_preset: bool = True):141 """142 Master multiple files in a folder143 """144 input_path = Path(input_folder)145 output_path = Path(output_folder)146 147 # Create output folder148 output_path.mkdir(parents=True, exist_ok=True)149 150 # Find audio files151 audio_extensions = ['.wav', '.mp3', '.flac', '.aiff', '.m4a']152 audio_files = []153 for ext in audio_extensions:154 audio_files.extend(input_path.glob(f'*{ext}'))155 156 if not audio_files:157 print(f"โ No audio files found in {input_folder}")158 return159 160 print(f"๐ Found {len(audio_files)} audio files")161 print(f"๐ค Output folder: {output_folder}\n")162 163 # Process each file164 results = []165 for i, audio_file in enumerate(audio_files, 1):166 print(f"\n{'='*60}")167 print(f"Processing {i}/{len(audio_files)}: {audio_file.name}")168 print(f"{'='*60}")169 170 output_file = output_path / f"{audio_file.stem}_mastered{audio_file.suffix}"171 172 result = self.master_audio(173 str(audio_file),174 str(output_file),175 preset_name=preset_name,176 auto_preset=auto_preset177 )178 179 if result:180 results.append({181 'file': audio_file.name,182 **result183 })184 185 # Summary186 print("\n" + "="*60)187 print("๐ BATCH MASTERING COMPLETE!")188 print("="*60)189 print(f"Processed: {len(results)} files")190 print(f"Output: {output_folder}")191 print("="*60 + "\n")192 193 return results194 195 def list_presets(self, category: Optional[str] = None):196 """List available presets"""197 print("\n๐ AVAILABLE PRESETS")198 print("="*60)199 200 if category:201 presets = self.preset_library.get_presets_by_category(category)202 print(f"Category: {category.upper()}")203 else:204 presets = self.preset_library.presets.values()205 print("All Categories")206 207 # Group by category208 by_category = {}209 for preset in presets:210 if preset.category not in by_category:211 by_category[preset.category] = []212 by_category[preset.category].append(preset)213 214 # Print grouped215 for cat, cat_presets in sorted(by_category.items()):216 print(f"\n๐ธ {cat.upper()}:")217 for preset in cat_presets:218 preset_key = preset.name.lower().replace(' ', '_')219 print(f" โข {preset_key:25s} - {preset.description}")220 221 print("\n" + "="*60 + "\n")222 223 def show_preset(self, preset_name: str):224 """Show detailed preset information"""225 self.preset_library.print_preset_info(preset_name)226 227 228def main():229 """230 Main CLI function231 """232 import argparse233 234 parser = argparse.ArgumentParser(235 description='AI-Powered Audio Mastering Engine',236 formatter_class=argparse.RawDescriptionHelpFormatter,237 epilog="""238Examples:239 # Analyze audio240 python main.py analyze input.wav241 242 # Master with auto preset selection243 python main.py master input.wav --auto244 245 # Master with specific preset246 python main.py master input.wav --preset edm_club_master247 248 # Master with custom output path249 python main.py master input.wav -o output.wav --preset hiphop_808_punch250 251 # Batch process folder252 python main.py batch input_folder/ output_folder/ --auto253 254 # List presets255 python main.py list-presets256 257 # Show preset details258 python main.py show-preset edm_club_master259 """260 )261 262 subparsers = parser.add_subparsers(dest='command', help='Command to run')263 264 # Analyze command265 analyze_parser = subparsers.add_parser('analyze', help='Analyze audio file')266 analyze_parser.add_argument('input', help='Input audio file')267 268 # Master command269 master_parser = subparsers.add_parser('master', help='Master audio file')270 master_parser.add_argument('input', help='Input audio file')271 master_parser.add_argument('-o', '--output', help='Output file path')272 master_parser.add_argument('-p', '--preset', help='Preset name to use')273 master_parser.add_argument('--auto', action='store_true', 274 help='Auto-select best preset')275 276 # Batch command277 batch_parser = subparsers.add_parser('batch', help='Batch process folder')278 batch_parser.add_argument('input_folder', help='Input folder')279 batch_parser.add_argument('output_folder', help='Output folder')280 batch_parser.add_argument('-p', '--preset', help='Preset name to use')281 batch_parser.add_argument('--auto', action='store_true',282 help='Auto-select preset for each file')283 284 # List presets285 list_parser = subparsers.add_parser('list-presets', help='List available presets')286 list_parser.add_argument('-c', '--category', help='Filter by category')287 288 # Show preset289 show_parser = subparsers.add_parser('show-preset', help='Show preset details')290 show_parser.add_argument('preset_name', help='Preset name')291 292 # Suggest presets293 suggest_parser = subparsers.add_parser('suggest', help='Suggest presets for audio')294 suggest_parser.add_argument('input', help='Input audio file')295 suggest_parser.add_argument('-n', '--num', type=int, default=3,296 help='Number of suggestions')297 298 args = parser.parse_args()299 300 if not args.command:301 parser.print_help()302 return303 304 # Initialize engine305 engine = MasteringEngine()306 307 # Execute command308 if args.command == 'analyze':309 engine.analyze_audio(args.input)310 311 elif args.command == 'master':312 engine.master_audio(313 args.input,314 output_path=args.output,315 preset_name=args.preset,316 auto_preset=args.auto317 )318 319 elif args.command == 'batch':320 engine.batch_master(321 args.input_folder,322 args.output_folder,323 preset_name=args.preset,324 auto_preset=args.auto325 )326 327 elif args.command == 'list-presets':328 engine.list_presets(category=args.category)329 330 elif args.command == 'show-preset':331 engine.show_preset(args.preset_name)332 333 elif args.command == 'suggest':334 engine.suggest_presets(args.input, num_suggestions=args.num)335 336 337if __name__ == "__main__":338 main()339 