Spaces:
Running
Running
| """ | |
| Tab Agent - Hugging Face Gradio Interface (MVP) | |
| AI-powered guitar/bass tablature transcription using Basic Pitch | |
| This is the web UI for the Tab Agent transcription system. | |
| Optimized for Zero GPU deployment with Basic Pitch model. | |
| """ | |
| import os | |
| import sys | |
| import tempfile | |
| import zipfile | |
| from pathlib import Path | |
| from datetime import datetime | |
| import gradio as gr | |
| import numpy as np | |
| # Zero GPU support for faster processing | |
| try: | |
| import spaces | |
| GPU_AVAILABLE = True | |
| except ImportError: | |
| GPU_AVAILABLE = False | |
| print("โ ๏ธ Running without Zero GPU support") | |
| # Import Tab Agent modules | |
| from agents import SplitterAgent, EarAgent, TabAgent | |
| from main import export_tab_to_txt, export_tab_to_json | |
| # Configuration | |
| TEMP_DIR = tempfile.gettempdir() | |
| OUTPUT_DIR = Path(TEMP_DIR) / "tab_agent_outputs" | |
| OUTPUT_DIR.mkdir(exist_ok=True) | |
| # Default tunings | |
| GUITAR_TUNING = [40, 45, 50, 55, 59, 64] # E2-A2-D3-G3-B3-E4 | |
| BASS_TUNING = [23, 28, 33, 38, 43] # B0-E1-A1-D2-G2 | |
| # Apply Zero GPU decorator if available | |
| if GPU_AVAILABLE: | |
| def process_audio( | |
| audio_file, | |
| instrument_type="Guitar", | |
| include_midi=True, | |
| include_tab=True, | |
| include_json=True, | |
| progress=gr.Progress() | |
| ): | |
| """ | |
| Process audio file and generate tablature. | |
| Args: | |
| audio_file: Path to uploaded audio file | |
| instrument_type: "Guitar" or "Bass" | |
| include_midi: Export MIDI files | |
| include_tab: Export ASCII tab files | |
| include_json: Export JSON files | |
| progress: Gradio progress callback | |
| Returns: | |
| Tuple of (status_message, output_files_zip) | |
| """ | |
| return _process_audio_impl(audio_file, instrument_type, include_midi, | |
| include_tab, include_json, progress) | |
| else: | |
| def process_audio( | |
| audio_file, | |
| instrument_type="Guitar", | |
| include_midi=True, | |
| include_tab=True, | |
| include_json=True, | |
| progress=gr.Progress() | |
| ): | |
| """Process audio file and generate tablature (CPU-only).""" | |
| return _process_audio_impl(audio_file, instrument_type, include_midi, | |
| include_tab, include_json, progress) | |
| def _process_audio_impl( | |
| audio_file, | |
| instrument_type, | |
| include_midi, | |
| include_tab, | |
| include_json, | |
| progress | |
| ): | |
| """ | |
| Internal implementation of audio processing. | |
| """ | |
| if audio_file is None: | |
| return "โ Please upload an audio file", None | |
| try: | |
| # Create unique output directory | |
| timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| session_dir = OUTPUT_DIR / f"session_{timestamp}" | |
| session_dir.mkdir(exist_ok=True) | |
| # Get file info | |
| audio_path = Path(audio_file) | |
| song_name = audio_path.stem | |
| progress(0.1, desc="๐ต Initializing agents...") | |
| # Initialize agents (auto-detects GPU via Zero GPU) | |
| splitter = SplitterAgent(output_dir=str(session_dir / "stems")) | |
| ear = EarAgent(device="auto") # Auto-detect: GPU if available, else CPU | |
| # Stage 1-3: Stem separation and processing | |
| progress(0.2, desc="๐ต Separating audio stems (Demucs)...") | |
| stems = splitter.separate_stems(str(audio_path)) | |
| progress(0.3, desc="๐ธ Processing guitar stems...") | |
| if instrument_type == "Guitar": | |
| guitar_stems = splitter.process_guitars(stems['guitar']) | |
| processed_stems = { | |
| "lead": guitar_stems['lead'], | |
| "rhythm_l": guitar_stems['left'], | |
| "rhythm_r": guitar_stems['right'] | |
| } | |
| else: # Bass | |
| bass_clean = splitter.process_bass(stems['bass']) | |
| processed_stems = {"bass": bass_clean} | |
| # Stage 4: Transcription | |
| progress(0.5, desc="๐ธ Transcribing to MIDI (Basic Pitch)...") | |
| results = {} | |
| for stem_name, stem_path in processed_stems.items(): | |
| progress(0.5 + (0.3 / len(processed_stems)), | |
| desc=f"๐ธ Transcribing {stem_name}...") | |
| # Transcribe | |
| notes_raw = ear.transcribe_stem( | |
| stem_path, | |
| target=instrument_type | |
| ) | |
| notes_clean = ear.humanize_and_clean( | |
| notes_raw, | |
| is_bass=(instrument_type == "Bass") | |
| ) | |
| # Export MIDI | |
| if include_midi: | |
| midi_path = session_dir / f"{song_name}_{stem_name}.mid" | |
| ear.export_midi(notes_clean, str(midi_path)) | |
| results[stem_name] = notes_clean | |
| # Stage 5: Tablature generation | |
| progress(0.8, desc="๐ Generating tablature...") | |
| if instrument_type == "Guitar": | |
| tab_agent = TabAgent(tuning=GUITAR_TUNING, num_frets=24) | |
| else: | |
| tab_agent = TabAgent(tuning=BASS_TUNING, num_frets=24) | |
| for stem_name, notes in results.items(): | |
| tab_data = tab_agent.generate_tab(notes) | |
| # Export tab files | |
| if include_tab: | |
| tab_path = session_dir / f"{song_name}_{stem_name}.tab" | |
| export_tab_to_txt( | |
| tab_data, | |
| str(tab_path), | |
| instrument=f"{instrument_type} - {stem_name}" | |
| ) | |
| if include_json: | |
| json_path = session_dir / f"{song_name}_{stem_name}.json" | |
| export_tab_to_json( | |
| tab_data, | |
| str(json_path), | |
| instrument=f"{instrument_type} - {stem_name}" | |
| ) | |
| # Create ZIP archive | |
| progress(0.9, desc="๐ฆ Creating download package...") | |
| zip_path = session_dir / f"{song_name}_tablature.zip" | |
| with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf: | |
| for file in session_dir.rglob("*"): | |
| if file.is_file() and file != zip_path: | |
| arcname = file.relative_to(session_dir) | |
| zipf.write(file, arcname) | |
| progress(1.0, desc="โ Complete!") | |
| # Generate status message | |
| file_count = len(list(session_dir.glob("*.*"))) - 1 # Exclude zip | |
| status_msg = f""" | |
| โ **Transcription Complete!** | |
| - **Song**: {song_name} | |
| - **Instrument**: {instrument_type} | |
| - **Files Generated**: {file_count} | |
| - **Formats**: {', '.join([ | |
| 'MIDI' if include_midi else '', | |
| 'Tab' if include_tab else '', | |
| 'JSON' if include_json else '' | |
| ]).strip(', ')} | |
| ๐ฅ **Download the ZIP file below to get all outputs!** | |
| """ | |
| return status_msg, str(zip_path) | |
| except Exception as e: | |
| import traceback | |
| error_msg = f"โ **Error during processing:**\n\n```\n{str(e)}\n\n{traceback.format_exc()}\n```" | |
| return error_msg, None | |
| # Create Gradio interface | |
| def create_ui(): | |
| """Create Gradio UI interface.""" | |
| with gr.Blocks( | |
| title="Tab Agent - AI Tablature Transcription", | |
| theme=gr.themes.Soft() | |
| ) as demo: | |
| gr.Markdown(""" | |
| # ๐ธ Tab Agent - AI Tablature Transcription (MVP) | |
| AI-powered transcription for guitar and bass using **Basic Pitch** (Spotify's proven model). | |
| ## Features | |
| - ๐ต **Multi-stage pipeline**: Demucs stem separation + spatial processing | |
| - ๐ค **Basic Pitch AI**: Production-ready transcription model (Spotify) | |
| - ๐ธ **Multi-track support**: Lead guitar, rhythm guitars (L/R), bass | |
| - ๐ **Multiple formats**: MIDI, ASCII tablature, JSON | |
| - ๐ฏ **Optimal fingering**: Dynamic programming for playable tabs | |
| - โจ **Technique detection**: Slides, hammer-ons, pull-offs | |
| - โก **Zero GPU**: Faster processing with Hugging Face Zero GPU | |
| --- | |
| """) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### Upload Audio") | |
| audio_input = gr.Audio( | |
| label="Audio File", | |
| type="filepath", | |
| sources=["upload"], | |
| ) | |
| instrument_type = gr.Radio( | |
| label="Instrument Type", | |
| choices=["Guitar", "Bass"], | |
| value="Guitar" | |
| ) | |
| gr.Markdown("### Export Options") | |
| with gr.Group(): | |
| export_midi = gr.Checkbox(label="MIDI Files", value=True) | |
| export_tab = gr.Checkbox(label="ASCII Tablature", value=True) | |
| export_json = gr.Checkbox(label="JSON Data", value=True) | |
| transcribe_btn = gr.Button( | |
| "๐ธ Transcribe to Tablature", | |
| variant="primary", | |
| size="lg" | |
| ) | |
| with gr.Column(scale=1): | |
| gr.Markdown("### Results") | |
| status_output = gr.Markdown( | |
| value="Upload an audio file and click 'Transcribe' to begin.", | |
| label="Status" | |
| ) | |
| download_output = gr.File( | |
| label="Download Results (ZIP)", | |
| interactive=False | |
| ) | |
| # Examples | |
| gr.Markdown("### Example Audio Files") | |
| gr.Examples( | |
| examples=[ | |
| ["examples/guitar_solo.wav", "Guitar"], | |
| ["examples/bass_groove.wav", "Bass"], | |
| ], | |
| inputs=[audio_input, instrument_type], | |
| ) | |
| # Information | |
| with gr.Accordion("โน๏ธ How It Works", open=False): | |
| gr.Markdown(""" | |
| ### Processing Pipeline | |
| 1. **Stem Separation (Demucs)**: Isolates guitar/bass from full mix | |
| 2. **Spatial Processing**: Separates lead and rhythm guitars using mid-side technique | |
| 3. **AI Transcription (Basic Pitch)**: Converts audio to MIDI notes with proven accuracy | |
| 4. **Tablature Generation**: Dynamic programming finds optimal fingering | |
| 5. **Technique Detection**: Identifies slides, hammer-ons, pull-offs | |
| ### Output Formats | |
| - **MIDI**: Import into DAWs (Reaper, Ableton, Logic, etc.) | |
| - **ASCII Tab**: Human-readable tablature for printing | |
| - **JSON**: Programmatic access for custom applications | |
| ### Tips for Best Results | |
| - Use high-quality audio (WAV/FLAC preferred) | |
| - Isolate guitar/bass tracks if possible | |
| - Shorter clips (< 60 seconds) process faster | |
| - Clean recordings work better than live/noisy audio | |
| """) | |
| with gr.Accordion("๐ Links & Resources", open=False): | |
| gr.Markdown(""" | |
| - **GitHub**: [Tab-Agent Repository](https://github.com/YOUR_USERNAME/Tab-Agent) | |
| - **ReaPack**: [Install for Reaper](https://github.com/YOUR_USERNAME/Tab-Agent#reaper-integration) | |
| - **Documentation**: [Full Guide](https://github.com/YOUR_USERNAME/Tab-Agent/blob/main/README.md) | |
| - **Basic Pitch**: [Spotify Research](https://github.com/spotify/basic-pitch) | |
| **License**: MIT | **Python**: 3.10+ | **Model**: Basic Pitch | **Acceleration**: Zero GPU | |
| """) | |
| # Connect event handlers | |
| transcribe_btn.click( | |
| fn=process_audio, | |
| inputs=[ | |
| audio_input, | |
| instrument_type, | |
| export_midi, | |
| export_tab, | |
| export_json | |
| ], | |
| outputs=[status_output, download_output] | |
| ) | |
| return demo | |
| # Main entry point | |
| if __name__ == "__main__": | |
| demo = create_ui() | |
| demo.queue() # Enable queuing for progress tracking | |
| demo.launch(server_name="0.0.0.0") | |