# SoundFont Auto-Load Feature

Auto-load soundfont on initialization without requiring user to click a load button!

## 🎯 Overview

The library can automatically load a default soundfont when the synthesizer initializes, eliminating the need for users to manually load soundfonts.

## 📦 Configuration

Configure via environment variables in your `.env` file:

```env
# Enable auto-load (set to 'true' to enable)
KAR_PLAYER_SOUNDFONT_AUTO_LOAD=true

# Path to your default soundfont file
KAR_PLAYER_SOUNDFONT_DEFAULT_PATH=/assets/sounds/GeneralUserGS.sf3

# Optional: Timeout for auto-load in milliseconds (default: 30000)
KAR_PLAYER_SOUNDFONT_AUTO_LOAD_TIMEOUT=30000
```

### Next.js Configuration

For Next.js projects, use the `NEXT_PUBLIC_` prefix:

```env
# .env.local or .env
NEXT_PUBLIC_KAR_PLAYER_SOUNDFONT_AUTO_LOAD=true
NEXT_PUBLIC_KAR_PLAYER_SOUNDFONT_DEFAULT_PATH=/assets/sounds/GeneralUserGS.sf3
NEXT_PUBLIC_KAR_PLAYER_SOUNDFONT_AUTO_LOAD_TIMEOUT=30000
```

## 🚀 Usage

### Automatic (Recommended)

Just initialize the synthesizer - the soundfont will load automatically!

```typescript
import { SpessaSynthWrapper } from '@karaplay/kar-player';

const audioContext = new AudioContext();
const synth = new SpessaSynthWrapper(audioContext);

// Initialize - soundfont auto-loads if env config is set!
await synth.initialize('/spessasynth_processor.min.js');

// ✅ Soundfont is already loaded! Start playing immediately!
await player.play();
```

### Manual Override

You can still manually load a different soundfont:

```typescript
// Auto-load happens during initialize()
await synth.initialize('/spessasynth_processor.min.js');

// Optionally override with a different soundfont
const customSoundFont = await fetch('/sounds/custom.sf2').then(r => r.arrayBuffer());
await synth.loadSoundFont(customSoundFont, 'custom');
```

## 🎬 How It Works

**Automatic Flow:**

```
1. User calls synth.initialize()
   ↓
2. Synthesizer initializes
   ↓
3. Check env config: SOUNDFONT_AUTO_LOAD === 'true'?
   ↓ YES
4. Fetch soundfont from SOUNDFONT_DEFAULT_PATH
   ↓
5. Load into synthesizer automatically
   ↓
6. ✅ Ready to play!
```

**Without Auto-Load (Manual):**

```
1. User calls synth.initialize()
   ↓
2. Synthesizer initializes
   ↓
3. ❌ User must manually call loadSoundFont()
   ↓
4. User clicks "Load SoundFont" button
   ↓
5. Fetch and load soundfont
   ↓
6. ✅ Ready to play
```

## 📊 Configuration Options

| Environment Variable | Type | Default | Description |
|---------------------|------|---------|-------------|
| `SOUNDFONT_AUTO_LOAD` | boolean | `false` | Enable auto-load |
| `SOUNDFONT_DEFAULT_PATH` | string | `null` | Path to soundfont file |
| `SOUNDFONT_AUTO_LOAD_TIMEOUT` | number | `30000` | Timeout in milliseconds |

## ✨ Benefits

✅ **Better UX** - No manual "Load SoundFont" button needed  
✅ **Faster startup** - Soundfont loads during initialization  
✅ **Configurable** - Easy enable/disable via `.env`  
✅ **Non-blocking** - Won't crash if auto-load fails  
✅ **Flexible** - Can still manually override  

## 🎯 Example: Next.js App

### 1. Setup Environment

```env
# .env.local
NEXT_PUBLIC_KAR_PLAYER_SOUNDFONT_AUTO_LOAD=true
NEXT_PUBLIC_KAR_PLAYER_SOUNDFONT_DEFAULT_PATH=/assets/sounds/GeneralUserGS.sf3
```

### 2. Place SoundFont

```
your-app/
  public/
    assets/
      sounds/
        GeneralUserGS.sf3  ← Place your soundfont here
```

### 3. Use in Component

```typescript
'use client';

import { useEffect, useState } from 'react';
import { SpessaSynthWrapper, MIDIPlayer } from '@karaplay/kar-player';

export default function KaraokePlayer() {
  const [synth, setSynth] = useState<SpessaSynthWrapper | null>(null);
  const [ready, setReady] = useState(false);

  useEffect(() => {
    const audioContext = new AudioContext();
    const wrapper = new SpessaSynthWrapper(audioContext);
    
    // Initialize - soundfont auto-loads!
    wrapper.initialize('/spessasynth_processor.min.js')
      .then(() => {
        setSynth(wrapper);
        setReady(true);
        console.log('✅ Ready to play! SoundFont auto-loaded.');
      })
      .catch(console.error);
  }, []);

  // No "Load SoundFont" button needed! Just play!
  const handlePlay = async () => {
    if (!synth || !ready) return;
    
    // Load and play KAR file immediately
    const karBuffer = await fetch('/songs/example.kar').then(r => r.arrayBuffer());
    // ... play logic
  };

  return (
    <div>
      <button onClick={handlePlay} disabled={!ready}>
        {ready ? 'Play Song' : 'Loading...'}
      </button>
    </div>
  );
}
```

## 🔧 Programmatic Control

### Check if Auto-Load is Enabled

```typescript
import { isSoundFontAutoLoadEnabled } from '@karaplay/kar-player';

if (isSoundFontAutoLoadEnabled()) {
  console.log('Auto-load is enabled!');
} else {
  console.log('Auto-load is disabled - manual load required');
}
```

### Get Default Path

```typescript
import { getDefaultSoundFontPath } from '@karaplay/kar-player';

const path = getDefaultSoundFontPath();
console.log('Default soundfont path:', path); // '/assets/sounds/GeneralUserGS.sf3'
```

### Get Timeout

```typescript
import { getSoundFontAutoLoadTimeout } from '@karaplay/kar-player';

const timeout = getSoundFontAutoLoadTimeout();
console.log('Auto-load timeout:', timeout); // 30000
```

## 🐛 Error Handling

Auto-load errors are **non-critical** - they won't crash your app:

```typescript
// Auto-load is attempted during initialize()
await synth.initialize('/spessasynth_processor.min.js');

// If auto-load fails, a warning is logged but app continues
// You can still manually load a soundfont:
if (!synth.soundBankLoaded) {
  console.warn('Auto-load failed, loading manually...');
  const buffer = await fetch('/sounds/fallback.sf2').then(r => r.arrayBuffer());
  await synth.loadSoundFont(buffer);
}
```

## 📈 Performance

| Aspect | Impact |
|--------|--------|
| **Startup Time** | +2-5s (depends on soundfont size) |
| **Network** | 1 extra HTTP request (~30-50MB) |
| **Memory** | +50-100MB (soundfont in memory) |
| **UX** | ✅ Better (no button click needed) |

## 🎯 Best Practices

### 1. Use Small SoundFonts
```env
# Good: Small, optimized soundfont (~30MB)
SOUNDFONT_DEFAULT_PATH=/sounds/GeneralUserGS.sf3

# Bad: Large, uncompressed soundfont (200MB+)
# SOUNDFONT_DEFAULT_PATH=/sounds/huge.sf2
```

### 2. Show Loading State
```typescript
const [loading, setLoading] = useState(true);

useEffect(() => {
  synth.initialize('/processor.js')
    .then(() => setLoading(false));
}, []);

return loading ? <Spinner /> : <Player />;
```

### 3. Provide Fallback
```typescript
// If auto-load is disabled, show manual load button
const autoLoadEnabled = isSoundFontAutoLoadEnabled();

return (
  <div>
    {!autoLoadEnabled && (
      <button onClick={handleManualLoad}>
        Load SoundFont
      </button>
    )}
    <button onClick={handlePlay} disabled={!synth?.soundBankLoaded}>
      Play
    </button>
  </div>
);
```

## 🔄 Migration Guide

### Before (Manual Load)

```typescript
// Old way - manual load
const synth = new SpessaSynthWrapper(audioContext);
await synth.initialize('/processor.js');

// User must click button
<button onClick={async () => {
  const sf = await fetch('/sounds/font.sf3').then(r => r.arrayBuffer());
  await synth.loadSoundFont(sf);
}}>
  Load SoundFont
</button>

// Then can play
<button onClick={play}>Play</button>
```

### After (Auto-Load)

```env
# .env - enable auto-load
NEXT_PUBLIC_KAR_PLAYER_SOUNDFONT_AUTO_LOAD=true
NEXT_PUBLIC_KAR_PLAYER_SOUNDFONT_DEFAULT_PATH=/sounds/font.sf3
```

```typescript
// New way - auto loads!
const synth = new SpessaSynthWrapper(audioContext);
await synth.initialize('/processor.js');
// ✅ SoundFont already loaded!

// Just play - no manual load button needed!
<button onClick={play}>Play</button>
```

## 🎉 Summary

- ✅ Set `SOUNDFONT_AUTO_LOAD=true` in `.env`
- ✅ Set `SOUNDFONT_DEFAULT_PATH` to your soundfont
- ✅ Initialize synthesizer as usual
- ✅ SoundFont loads automatically!
- ✅ No manual "Load SoundFont" button needed!

**That's it!** Your karaoke app now auto-loads soundfonts on startup! 🎤🎵
