--- tags: [gradio-custom-component, AudioGrid, audio-processing, custom-component-track] title: gradio_audiogrid short_description: Drag and drop component for audio files colorFrom: blue colorTo: yellow sdk: gradio pinned: false app_file: space.py --- # `gradio_audiogrid` **AudioGrid** is a custom Gradio component that provides an intuitive drag-and-drop interface for managing audio files. Perfect for audio processing workflows, music production, and any application that needs to handle multiple audio files. ## Features - 🎵 **Drag & Drop Audio Upload** - Intuitive file upload with visual feedback - 🎯 **Smart Reordering** - Drag files anywhere to reorder them intelligently - 📱 **Mobile Responsive** - Works great on desktop, tablet, and mobile - 🎨 **Beautiful UI** - Clean, modern interface with proper spacing - 🔄 **Flexible Positioning** - Drop files anywhere, not just on exact squares - ❌ **Easy Removal** - One-click file removal - 📁 **Multiple Formats** - Supports MP3, WAV, OGG, FLAC, AAC, M4A --- ## Installation ```bash pip install gradio_audiogrid ``` ## Usage ```python import tempfile import gradio as gr import numpy as np import soundfile as sf from gradio_audiogrid import AudioGrid def merge_audio_files(file_objects): """ Merge audio files by concatenating their audio data using soundfile Args: file_objects: List of file objects from AudioMerger component Returns: tuple: (sample_rate, merged_audio_array) for Gradio Audio component or (None, None) if merging fails """ if not file_objects or len(file_objects) == 0: return None, "❌ No audio files to merge" if len(file_objects) == 1: return None, "❌ Please upload at least 2 audio files to merge" try: _merged_audio = [] sample_rate = None for i, file_path in enumerate(file_objects): # Read audio file audio_data, sr = sf.read(file_path) if sample_rate is None: sample_rate = sr elif sr != sample_rate: ratio = sample_rate / sr new_length = int(len(audio_data) * ratio) audio_data = np.interp( np.linspace(0, len(audio_data), new_length), np.arange(len(audio_data)), audio_data ) # Convert stereo to mono if needed (take average of channels) if len(audio_data.shape) > 1: audio_data = np.mean(audio_data, axis=1) _merged_audio.append(audio_data) if not _merged_audio: return None, "❌ No valid audio files found" # Concatenate all audio arrays final_audio = np.concatenate(_merged_audio) # Create temporary file for the merged audio with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as tmp_file: sf.write(tmp_file.name, final_audio, sample_rate) merged_file_path = tmp_file.name # Create status message total_duration = len(final_audio) / sample_rate status = f"""✅ Successfully merged {len(file_objects)} audio files! 🎵 Result: • Total duration: {total_duration:.2f} seconds • Sample rate: {sample_rate} Hz • Channels: Mono • Format: WAV 🎧 Listen to the merged audio below!""" return merged_file_path, status except Exception as e: error_msg = f"❌ Error merging audio files: {str(e)}" return None, error_msg def reset_everything(): """Reset all components to initial state""" return [], None, "Ready to merge audio files! Upload at least 2 files to get started." def update_file_status(file_objects): """Update the live status based on uploaded files""" if len(file_objects) == 0: return "No valid files uploaded" elif len(file_objects) == 1: return "📁 1 file uploaded" else: return f"📁 {len(file_objects)} files ready" # Create the Gradio interface with gr.Blocks(title="🎵 Professional Audio Merger") as demo: gr.Markdown(""" # 🎵 Professional Audio Grid Upload multiple audio files and sort them. You can apply any effect you want. **Features:** - 🎯 **Drag & Drop**: Upload by dragging files or clicking - 🔄 **Reorder**: Drag uploaded files to change merge order - 📊 **Multiple Formats**: Supports MP3, WAV, FLAC, OGG, M4A, AAC """) with gr.Row(): with gr.Column(scale=2): gr.Markdown("### 📁 Upload & Arrange Audio Files") audio_files = AudioGrid( value=[], label="Drag files here or click to upload", interactive=True, elem_classes=["audio-merger-container"] ) # Live status update file_status = gr.Textbox( value="No files uploaded yet", label="📊 Upload Status", interactive=False, lines=2 ) with gr.Column(scale=1): gr.Markdown("### 🎛️ Controls") merge_btn = gr.Button( "🎵 Merge Audio Files", variant="primary", size="lg", elem_classes=["merge-button"] ) clear_btn = gr.Button( "🗑️ Clear All", variant="secondary", size="lg" ) gr.Markdown(""" **Instructions:** 1. Upload 2+ audio files 2. Drag to reorder if needed 3. Click "Merge Audio Files" 4. Listen to the result! """) gr.Markdown("### 🎧 Merged Audio Result") with gr.Row(): with gr.Column(scale=2): # Status/info output merge_status = gr.Textbox( value="Ready to merge audio files! Upload at least 2 files to get started.", label="🔍 Merge Status & Details", interactive=False, lines=8 ) with gr.Column(scale=2): # Audio output merged_audio = gr.Audio( label="🎵 Merged Audio Player", type="filepath", interactive=False ) # Event handlers merge_btn.click( fn=merge_audio_files, inputs=audio_files, outputs=[merged_audio, merge_status] ) clear_btn.click( fn=reset_everything, outputs=[audio_files, merged_audio, merge_status] ) # Live update of file status audio_files.change( fn=update_file_status, inputs=audio_files, outputs=file_status ) gr.Markdown(""" --- ### 🔧 Technical Details - **Audio Processing**: Uses SoundFile library for high-quality audio handling - **Concatenation**: Audio arrays are concatenated using NumPy for efficiency - **Format Handling**: Automatically converts different sample rates and channels - **Output**: Generates WAV format for maximum compatibility - **Memory Efficient**: Processes files sequentially to minimize RAM usage **Supported Input Formats**: MP3, WAV, FLAC, OGG, M4A, AAC, and more **Output Format**: WAV (uncompressed, high quality) """) if __name__ == "__main__": demo.launch(debug=True) ``` ## `AudioGrid` ### Initialization
| name | type | default | description |
|---|---|---|---|
value |
```python list[str] | Callable | None ``` | None |
default list of audio file paths. If a function is provided, the function will be called |
label |
```python str | I18nData | None ``` | None |
the label for this component, |
every |
```python Timer | float | None ``` | None |
Continuously calls `value` to recalculate it if `value` is a |
inputs |
```python Component | Sequence[Component] | set[Component] | None ``` | None |
Components that are used as inputs to calculate `value` if |
show_label |
```python bool | None ``` | None |
if True, will display label. scale: relative size compared to adjacent Components. For example if |
scale |
```python int | None ``` | None |
relative size compared to adjacent Components. For example if |
min_width |
```python int ``` | 160 |
minimum pixel width, will wrap if not sufficient screen space to satisfy this value. If a certain |
interactive |
```python bool | None ``` | None |
if True, will be rendered as an editable upload area; if False, editing will be |
visible |
```python bool ``` | True |
If False, component will be hidden. elem_id: An optional string that is assigned as the id of this |
elem_id |
```python str | None ``` | None |
An optional string that is assigned as the id of this |
elem_classes |
```python list[str] | str | None ``` | None |
An optional list of strings |
render |
```python bool ``` | True |
If False, component will not render be rendered in the Blocks context. Should be used if the |
key |
```python int | str | tuple[int | str, ...] | None ``` | None |
in a gr.render, Components |
preserved_by_key |
```python list[str] | str | None ``` | "value" |
A list of parameters from this |