CoolFace
Apppublic

Shahzroux/audio-mastering-engine

sourceHugging Faceapache-2.0updated 7mo agoView on Hugging Face
0likes
main.py339 linesDownload Raw Back to src
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